package com.heima.wemedia.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.injector.methods.SelectById;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.common.constants.ChannelConstants;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.dtos.ChannelDto;
import com.heima.model.wemedia.pojos.WmChannel;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.wemedia.mapper.WmChannelMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.service.WmChannelService;
import com.heima.wemedia.service.WmNewsService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;

@Service
@Transactional
@Slf4j
public class WmChannelServiceImpl extends ServiceImpl<WmChannelMapper, WmChannel> implements WmChannelService {

	/**
	 * 查询所有频道
	 *
	 * @return
	 */
	@Override
	public ResponseResult findAll() {
		return ResponseResult.okResult(list());
	}

	/**
	 * 新增保存频道
	 *
	 * @param adChannel
	 * @return
	 */
	@Override
	public ResponseResult insert(WmChannel adChannel) {
		// 1. 检查参数,如果为空，说明客户端没有提供有效的频道信息，此时方法直接返回一个错误结果，表示参数无效
		if (adChannel == null) {
			//提示参数非法
			return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
		}
		// 检查是否存在频道名称相同的情况
		// 首先根据名称查询数据库中是否存在相同名称的频道
		WmChannel channel = getOne(Wrappers.<WmChannel>lambdaQuery().eq(WmChannel::getName, adChannel.getName()));
		//如果查询结果channel不为空，说明数据库中已存在相同名称的频道,频道已存在不需要再新增频道
		if (channel != null) {
			//提示频道已存在
			return ResponseResult.errorResult(AppHttpCodeEnum.DATA_EXIST, "频道已存在");
		}

		// 2. 保存频道，如果通过了参数检查和名称重复性检查，说明可以安全地将新的频道信息保存到数据库中
		//设置了要保存频道的创建时间，使用的是当前的系统时间，这个数据不是前端传递的，需要设置默认值
		adChannel.setCreatedTime(new Date());
		adChannel.setIsDefault(true);
		//调用了继承自ServiceImpl的save方法，将频道对象保存到数据库
		save(adChannel);

		//当上述步骤都顺利完成，表示频道已成功插入到数据库中
		return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
	}

	/**
	 * 根据频道名称模糊分页查询
	 *
	 * @param dto
	 * @return
	 */
	@Override
	//主要功能是根据用户提供的频道名称进行模糊查询，并根据分页参数返回指定页码和大小的查询结果
	//首先会验证传入的参数的有效性，然后构建查询条件和分页对象，执行查询并将结果封装后返回客户端
	public ResponseResult findByNameAndPage(ChannelDto dto) {
		// 1. 首先，代码检查传入的ChannelDto对象是否为null
		if (dto == null) {
			//如果为null，说明客户端没有提供有效的查询参数
			return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
		}

		// 2. 检查分页，调用PageRequestDto的checkParam方法检查参数
		//dto是ChannelDto的实例化对象，ChannelDto又继承了PageRequestDto类
		//所以dto可以调用PageRequestDto类中的checkParam方法检查参数
		dto.checkParam();

		// 3. 模糊查询 + 分页
		//创建一个IPage对象，该对象用于存储分页查询的结果，用传入页码和每页大小来初始化对象
		IPage page = new Page(dto.getPage(), dto.getSize());
		// 频道名称模糊查询，使用LambdaQueryWrapper来构建查询条件，这里使用的是like方法，它可以在频道名称中进行模糊匹配
		LambdaQueryWrapper<WmChannel> lambdaQueryWrapper = new LambdaQueryWrapper<>();
		if (StringUtils.isNotBlank(dto.getName())) {
			//如果dto.getName()不为空，则将此名称作为查询条件的一部分
			lambdaQueryWrapper.like(WmChannel::getName, dto.getName());
		}

		//调用继承自ServiceImpl的page方法，传入上面创建的分页对象和查询条件，执行分页查询
		//page方法会根据传入的分页条件和查询条件，从数据库中查询出符合条件的数据，并填充到分页对象中
		page = page(page, lambdaQueryWrapper);

		// 4. 结果返回
		//查询完成后，根据分页对象中的信息创建一个ResponseResult对象，该对象用于封装返回给客户端的结果
		ResponseResult responseResult = new PageResponseResult(dto.getPage(), dto.getSize(), (int) page.getTotal());
		//将分页查询到的数据记录设置到ResponseResult对象中
		responseResult.setData(page.getRecords());

		return responseResult;
	}

	@Autowired
	private WmNewsService wmNewsService;

	/**
	 * 修改频道信息
	 *
	 * @param adChannel
	 * @return
	 */
	@Override
	//这段代码的主要功能是更新媒体频道的信息
	//在更新前会进行参数检查和是否被已发布文章引用的检查，以确保数据的完整性和业务逻辑的正确性
	//如果参数无效或频道被已发布的文章引用，操作将被拒绝并返回相应的错误信息
	public ResponseResult update(WmChannel adChannel) {
		// 首先，方法检查传入的adChannel对象是否为null
		// 其次，检查adChannel对象的id属性是否为null
		if (adChannel == null || adChannel.getId() == null) {
			//提示参数非法
			return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
		}

		// 2. 判断频道是否被已发布的文章引用
		//通过调用wmNewsService.count方法，构建了一个查询条件LambdaQueryWrapper
		int count = wmNewsService.count(Wrappers
				.<WmNews>lambdaQuery()
				//WmNews::getChannelId等于adChannel.getId()和WmNews::getStatus等于已发布状态的代码
				.eq(WmNews::getChannelId, adChannel.getId())
				.eq(WmNews::getStatus, WmNews.Status.PUBLISHED.getCode()));
		//执行查询后，获取到已发布文章引用该频道的记录数count

		//如果count大于0，说明该频道已经被已发布的文章引用，不能修改或禁用
		if (count > 0) {
			//方法返回一个错误结果ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID, "频道被引用不能修改或禁用")
			return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID, "频道被引用不能修改或禁用");
		}

		// 3. 修改,updateById方法继承自ServiceImpl，用于根据adChannel对象的id属性更新数据库中对应的频道信息
		updateById(adChannel);

		// 4. 返回结果
		return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
	}

	/**
	 * 删除频道
	 *
	 * @param id
	 * @return
	 */
	@Override
	public ResponseResult delete(Integer id) {

		//首先判断参数是否为空
		if (id == null || id < 0) {
			return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
		}

		//根据id去数据库中查询频道信息
		WmChannel wmChannel = getById(id);
		if(wmChannel == null) {
			return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
		}

		//判断频道信息中状态字段是否为1，为1则表示不允许删除，抛出异常
		if (wmChannel.getStatus() == ChannelConstants.Channel_status_start) {
			return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID, "无法删除未被禁用的频道");
		}

		// 4. 判断频道是否被已发表的文章引用
		int count = wmNewsService.count(Wrappers
				.<WmNews>lambdaQuery()
				.eq(WmNews::getChannelId, wmChannel.getId())
				.eq(WmNews::getStatus, WmNews.Status.PUBLISHED.getCode()));
		if(count > 0) {
			return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID, "频道被引用，不能删除");
		}

		//程序运行到此处，表示可以正常执行删除操作，按照id删除指定频道
		removeById(id);

		return ResponseResult.okResult("频道删除成功");
	}

}